package com.demo.task.ui.screen

import android.content.Context
import android.util.Log
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.annotation.OptIn
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.net.toUri
import androidx.media3.common.MediaItem
import androidx.media3.common.PlaybackException
import androidx.media3.common.Player
import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.DefaultRenderersFactory
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.exoplayer.mediacodec.MediaCodecSelector
import androidx.media3.exoplayer.mediacodec.MediaCodecUtil
import androidx.media3.ui.PlayerView
import com.demo.task.bean.PlanBean
import com.demo.task.ext.getResPath
import com.demo.task.ext.getUrlFileName
import com.demo.task.ext.pxToDp
import com.demo.task.ext.sendError
import com.demo.task.ext.sendIds
import com.demo.task.socket.RemoteInterface
import com.demo.task.vm.MainViewModel
import com.orhanobut.logger.Logger
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import java.io.File

private const val TAG = "SyncScreen"

@OptIn(UnstableApi::class)
@Composable
fun SyncScreen(
    boxList: List<PlanBean.Plan.Group.Page.Box>,
    viewModel: MainViewModel,
    socketIO: RemoteInterface
) {
    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(Color.Black)
    ) {
        val context = LocalContext.current

        var window by remember { mutableStateOf<PlanBean.Plan.Group.Page.Box.Window?>(null) }
        var index by remember { mutableIntStateOf(0) }
        var mark by remember { mutableStateOf(false) }
        val coroutineScope = rememberCoroutineScope()

        val webViews by remember {
            mutableStateOf(
                List(3) {
                    WebView(context).apply {
                        setBackgroundColor(0)
                        webViewClient = WebViewClient()
                    }
                }.toList()
            )
        }

        var playerList = remember { emptyList<Player>() }
        val playListener = remember {
            var endCount by mutableIntStateOf(0)
            var playCount by mutableIntStateOf(0)
            val list = boxList[0].list
            object : Player.Listener {
                private var mediaId = ""
                private fun notifyBoxId() {
                    runBlocking {
                        boxList.forEach { box ->
                            viewModel.notifyAwait(box.id)
                        }
                    }
                }

                override fun onPlaybackStateChanged(playbackState: Int) {
                    super.onPlaybackStateChanged(playbackState)
                    when (playbackState) {
                        Player.STATE_ENDED -> {
                            nextPlay()
                        }

                        Player.STATE_READY -> {
                            Log.w(
                                TAG,
                                "onPlaybackStateChanged: STATE_READY ${playerList.size}"
                            )
                            if (++playCount % 3 == 0) {
                                playerList.forEach { media3 ->
                                    media3.play()
                                }
                            }
                        }

                        else -> {}
                    }
                }


                override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
                    super.onMediaItemTransition(mediaItem, reason)
                    mediaId = mediaItem?.mediaId.orEmpty()
                    socketIO.sendIds(mediaId)
                    Logger.w("onMediaItemTransition: $mediaId")
                    Log.w(TAG, "onMediaItemTransition: $mediaId")
                }

                override fun onPlayerError(error: PlaybackException) {
                    super.onPlayerError(error)
                    Logger.e(error, "onPlayerError: $mediaId ${error.message}")
                    Log.e(TAG, "onPlayerError: $mediaId ", error)
                    socketIO.sendError(key = mediaId, remark = error.message.orEmpty())
                    nextPlay()
                }

                private fun nextPlay() {
                    if (++endCount % 3 == 0) {
                        if ((++index % list.size) == 0) {
                            index = 0
                            notifyBoxId()
                        }

                        var win = list[index]
                        if (win.type == 3) {
                            coroutineScope.launch {
                                window = win
                                while (true) {
                                    webViews.forEach {
                                        it.loadData(
                                            win.content,
                                            "text/html",
                                            "UTF-8"
                                        )
                                    }
                                    delay(win.hold * 1000L)

                                    if ((++index % list.size) == 0) {
                                        index = 0
                                        notifyBoxId()
                                    }

                                    win = list[index]
                                    if (win.type != 3) {
                                        window = win
                                        playVideo(win, playerList)
                                        break
                                    }
                                }
                            }

                        } else {
                            playVideo(win, playerList)
                        }
                    }
                }

            }
        }

        val players by remember {
            mutableStateOf(
                List(3) {
                    createExoPlayer(context, playListener)
                }.toList().also {
                    playerList = it
                }
            )
        }

        LaunchedEffect(boxList) {
            launch {
                viewModel.gpsManager.visOverlay.collectLatest {
                    mark = !it
                }
            }

            launch {
                var win = boxList.firstOrNull()?.list?.firstOrNull() ?: return@launch
                Log.w(TAG, "launch window: $window")
                window = win
                when (win.type) {
                    3 -> {
                        launch {
                            val list = boxList[0].list
                            while (true) {
                                webViews.forEach {
                                    it.loadData(win.content, "text/html", "UTF-8")
                                }
                                delay(win.hold * 1000L)

                                if ((++index % list.size) == 0) {
                                    index = 0
                                }
                                win = list[index]
                                if (win.type != 3) {
                                    window = win
                                    playVideo(win, players)
                                    break
                                }
                            }
                        }
                    }

                    else -> {
                        playVideo(win, players)
                    }
                }
            }
        }

        Box(Modifier.fillMaxSize()) {
            window?.also {
                Log.w(TAG, "window: $window")
                repeat(boxList.size) { index ->
                    val box = boxList[index]
                    Box(
                        Modifier
                            .padding(start = box.x.pxToDp().dp, top = box.y.pxToDp().dp)
                            .size(box.pixelsW.pxToDp().dp, box.pixelsH.pxToDp().dp)
                    ) {
                        when (window?.type) {
                            3 -> AndroidView(factory = { webViews[index] })
                            else -> AndroidView(factory = {
                                PlayerView(context)
                                    .apply {
                                        layoutParams = ViewGroup.LayoutParams(
                                            MATCH_PARENT,
                                            MATCH_PARENT
                                        )
                                        useController = false
                                        player = players[index]
                                    }
                            })
                        }

                        if (index == 1 && mark) {
                            Spacer(
                                modifier = Modifier
                                    .fillMaxSize()
                                    .background(Color.Black)
                            )
                        }
                    }
                }
            }
        }

        DisposableEffect(Unit) {
            Log.w(TAG, "SyncScreen: DisposableEffect")
            onDispose {
                webViews.forEach {
                    it.destroy()
                }
                players.forEach {
                    it.release()
                }
            }
        }
    }
}

@OptIn(UnstableApi::class)
private fun createExoPlayer(context: Context, listener: Player.Listener): Player {
    val codecSelector = MediaCodecSelector { mimeType, secureDecoder, tunnelingDecoder ->
        MediaCodecUtil.getDecoderInfos(mimeType, secureDecoder, tunnelingDecoder).filter {
            it.hardwareAccelerated && !it.softwareOnly
        }
    }

    val renderersFactory = DefaultRenderersFactory(context)
        .setMediaCodecSelector(codecSelector)
        .setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER)
        .forceEnableMediaCodecAsynchronousQueueing()

    return ExoPlayer.Builder(context, renderersFactory)
        .build()
        .apply {
            addListener(listener)
        }
}

@OptIn(UnstableApi::class)
private fun playVideo(
    win: PlanBean.Plan.Group.Page.Box.Window,
    players: List<Player>
) {
    val fileName = win.content.getUrlFileName()
    val file = File(getResPath(), fileName)
    val mediaItem = MediaItem.Builder()
        .setUri(file.toUri())
        .setMediaId(win.id)
        .apply {
            if (win.type == 2) {
                setImageDurationMs(win.hold * 1000L)
            }
        }
        .build()
    players.forEach { media3 ->
        media3.setMediaItem(mediaItem)
        media3.prepare()
    }
}
